
#include <linux/module.h>

#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/miscdevice.h>
#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/mutex.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/stat.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/tty.h>
#include <linux/kmod.h>
#include <linux/gfp.h>

#include <linux/mod_devicetable.h>
#include <linux/of_device.h>

#include <linux/gfp.h>
#include <linux/gpio/consumer.h>
#include <linux/platform_device.h>
#include <linux/of_gpio.h>
#include <linux/of_irq.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/slab.h>
#include <linux/fcntl.h>
#include <linux/timer.h>
#include <linux/workqueue.h>
#include <asm/current.h>
#include <linux/delay.h>
#include <linux/version.h>


static int major;
static struct class *hs0038_class;
static struct gpio_desc *hs0038_gpiod;
static unsigned char hs0038_data = 0;
static int signal = 0;

static int irq;
static u64 irq_edge_time[100];
static int irq_cnt = 0;


static unsigned char hs0038_data_buf[8]; 
static unsigned char r = 0, w = 0;


static void put_data(unsigned char data)
{
	if (((w + 1) & 0x7) != r)
	{
		hs0038_data_buf[w] = data;
		w = (w + 1) & 0x7;
	}
}


static int get_data(unsigned char *data)
{
	if (w != r)
	{
		*data = hs0038_data_buf[r];
		r = (r + 1) & 0x7;
		return 0;
	}
	else 
	{
		return -1;
	}

}

static int has_data(void)
{
	if (w != r)
		return 1;
	else 
		return 0;
}




static DECLARE_WAIT_QUEUE_HEAD(hs0038_wait_queue);  			 /*创建并初始化等待队列*/

static int hs0038_open(struct inode *node, struct file *file)
{
	printk("%s %s  line %d\n", __FILE__, __FUNCTION__, __LINE__);
	return 0;
}



//针对红外遥控器，只能使用中断输出值

static ssize_t hs0038_read(struct file *file, char __user *buf, size_t size, loff_t *offsize)
{

	int ret;
	unsigned char val;
	wait_event_interruptible(hs0038_wait_queue, has_data());
	get_data(&val);
	ret = copy_to_user(buf, &val, 1);
	return 1;
}

static int hs0038_close(struct inode *node, struct file *file)
{
	printk("%s %s  line %d\n", __FILE__, __FUNCTION__, __LINE__);
	return 0;
}


static struct file_operations hs0038_ops = {
	.owner  = THIS_MODULE,
	.open    = hs0038_open,
	.release = hs0038_close,
	.read    = hs0038_read,
};

/* 0 : 成功(包括重复码), *val中记录数据
 * -2: 没接收完毕
 * -1: 解析错误
 */


static int hs0038_parse_data(void)
{
	int i, j, m;
	u64 tmp;
	unsigned char data[4];

	/*接收到重复码*/
	if (irq_cnt == 4)
	{
		tmp = irq_edge_time[1] - irq_edge_time[0];
		if (tmp > 8000000 && tmp < 10000000)
		{
			tmp = irq_edge_time[2] - irq_edge_time[1];
			if (tmp < 3000000)
			{
				return 0;
			}
		}
	}

	if (irq_cnt >= 68)
	{
		m = 3;
		for (i = 0; i < 4; i++)
		{
			data[i] = 0;
			for (j = 0; j < 8; j++)
			{
				if (irq_edge_time[m + 1] -irq_edge_time[m] > 1000000)
					data[i] |= (1<<j);
				m += 2;
			}
		}

		data[1] = ~data[1];
		data[3] = ~data[3];
		
		if ((data[0] == data[1]) && (data[2] == data[3]))
		{
			hs0038_data = data[2];
			return 0;
		}
		else 
			return -1;
	}
	else
	{
		return -2;  //函数必须要有返回值，中断函数不对这个返回值进行判断
	}
}

#if 0
static irqreturn_t hs0038_isr(int num, void *dev)
{
	int ret;

#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 0, 0))
	irq_edge_time[irq_cnt++] = ktime_get_boottime_ns();
#else
	irq_edge_time[irq_cnt++] = ktime_get_boot_ns();
#endif
	/*判断是否超时*/
	if (irq_cnt >= 2)
	{
		if (irq_edge_time[irq_cnt-1] - irq_edge_time[irq_cnt -2] > 30000000)
		{
			irq_edge_time[0] = irq_edge_time[irq_cnt - 1];
			irq_cnt = 1;
			return IRQ_HANDLED;
		}
	}

	ret = hs0038_parse_data();
	if (ret == 0)
	{
		irq_cnt == 0;
		signal = 1;
		wake_up(&hs0038_wait_queue);
	}
	else if (ret == 1)
	{
		irq_cnt == 0;
		signal = 1;
		wake_up(&hs0038_wait_queue);
	}
	else if (ret == -1)
	{
		irq_cnt == 0;
	}	
		
	return IRQ_HANDLED;
}

#endif

static irqreturn_t hs0038_isr(int num, void *dev)
{
	int ret;

#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 0, 0))
	irq_edge_time[irq_cnt++] = ktime_get_boottime_ns();
#else
	irq_edge_time[irq_cnt++] = ktime_get_boot_ns();
#endif
	/*判断是否超时*/
	if (irq_cnt >= 2)
	{
		if (irq_edge_time[irq_cnt-1] - irq_edge_time[irq_cnt -2] > 30000000)
		{
			irq_edge_time[0] = irq_edge_time[irq_cnt - 1];
			irq_cnt = 1;
			return IRQ_HANDLED;
		}
	}

	ret = hs0038_parse_data();
	if (ret == 0)
	{
		irq_cnt = 0;
		put_data(hs0038_data);
		wake_up(&hs0038_wait_queue);
	}
	else if (ret == -1)
	{
		irq_cnt = 0;
	}	
		
	return IRQ_HANDLED;
}




static struct of_device_id of_hs0038_match_table[] = {
	{ .compatible = "zmq,hs0038", },
	{ },
};

static int hs0038_probe(struct platform_device *pdev)
{
	int ret;
	printk("%s %s  line %d\n", __FILE__, __FUNCTION__, __LINE__);
	
	hs0038_gpiod = gpiod_get(&pdev->dev, NULL, 0);

	irq = gpiod_to_irq(hs0038_gpiod);
	ret = request_irq(irq, hs0038_isr, IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, "zmq_hs0038_irq", NULL);
	
	device_create(hs0038_class, NULL, MKDEV(major, 0), NULL, "hs0038_zmq");
	return 0;
	
}


static int hs0038_remove(struct platform_device *dev)
{
	printk("%s %s  line %d\n", __FILE__, __FUNCTION__, __LINE__);
	device_destroy(hs0038_class, MKDEV(major, 0));
	gpiod_put(hs0038_gpiod);   //释放gpio
	free_irq(irq, NULL);
	
	return 0;
}


static struct platform_driver hs0038_platform_drv = {
	.probe = hs0038_probe,
	.remove = hs0038_remove,
	.driver = 	{
		.name = "hs0038",
		.of_match_table = of_hs0038_match_table,
	},

};



static  int  hs0038_init(void)
{
	printk("%s %s  line %d\n", __FILE__, __FUNCTION__, __LINE__);
	
	major = register_chrdev(0, "zmq_hs0038_chrdrv", &hs0038_ops);
	hs0038_class = class_create(THIS_MODULE, "zmq_hs0038_class");
	if(IS_ERR(hs0038_class))
	{
		unregister_chrdev(major, "zmq_hs0038_chrdrv");
		return PTR_ERR(hs0038_class);
	}
	platform_driver_register(&hs0038_platform_drv);
	
	return 0;
}


static void hs0038_exit(void)
{
	printk("%s %s  line %d\n", __FILE__, __FUNCTION__, __LINE__);

	platform_driver_unregister(&hs0038_platform_drv);
	class_destroy(hs0038_class);
	unregister_chrdev(major, "zmq_hs0038_chrdev");
}


module_init(hs0038_init);
module_exit(hs0038_exit);
MODULE_LICENSE("GPL");






